//System Includes
#include <Windows.h>

//Local Includes
#include "Definitions.h"
#include "DirectX11.h"
#include "Renderable.h"
#include "Plane.h"
#include "View.h"
#include "XTime.h"
#include <sstream>
#include <string>

#include "Light.h"

#define LEAK_TEST 1


BOOL IsAlreadyRunning();
BOOL RegisterWindowClass(HINSTANCE hInstance);
HWND MakeWindow(HINSTANCE hInstance);
LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
void HandleInput(View* camera,float dt);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmdLine, int cmdShow){
	MSG msg;
	HWND mainHWnd;

	//Don't allow more than one instance to run
	//Comment out if more instances needed (client/server)
	if(hPrevInstance == 0){
		if(IsAlreadyRunning() == TRUE)
			return -1;
	}

	if(RegisterWindowClass(hInstance) == FALSE){
		ERRORMESSAGE(L"ERROR: Failed to register the window class.");
		return -2;
	}

	mainHWnd = MakeWindow(hInstance);

	if(mainHWnd == 0){
		ERRORMESSAGE(L"ERROR: Failed to create the Main Window");

		UnregisterClass(WINDOW_CLASS_NAME,hInstance);

		return -3;
	}

	ShowWindow(mainHWnd, cmdShow);
	UpdateWindow(mainHWnd);

	//Initialize other class(game/wrapper) here
	DirectX11* d3d = DirectX11::GetInstance();
	d3d->Initialize(mainHWnd);


	const int numObjs = 1;
	Plane* groups[numObjs] = {};
	for(int i = 0; i < numObjs;i++)
		groups[i] = new Plane(PosXYZ((rand() % 100)-50,(rand() % 100)-100,(rand() % 100)-50));

	Plane ground(PosXYZ(0,0,0));
	View camera;
	Light light;
	XTime timer;
	XTime rotTimer;
	XTime FPSTimer;
	int FPSCounter = 0;
	light.Initialize(ColorRGB(255,255,255));
	camera.Initialize();

	///////////////////////////////////////////


	//Main event loop
	while(true){
		if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE ) == TRUE){
			if(msg.message == WM_QUIT)
				break;

			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else {
			//Run game/wrapper logic here
			timer.Signal();
			rotTimer.Signal();
			FPSTimer.Signal();

			HandleInput(&camera,(float)timer.Delta());

			light.ApplyLight();
			light.UpdateLight((float)timer.TotalTime());
			d3d->Draw(ground.GetRenderable(),&camera);

			for(int i = 0; i < numObjs;i++)
				d3d->Draw(groups[i]->GetRenderable(),&camera);

			light.ApplyLight();
			d3d->RenderDebug(&camera);
			FPSCounter++;
			if(FPSTimer.TotalTime() >= 1.0){
				std::wostringstream fps;
				fps << "FPS: " << FPSCounter << '\n';
				//OutputDebugString(fps.str().c_str());
				SetWindowText(mainHWnd,fps.str().c_str());
				FPSTimer.Restart();
				FPSCounter = 0;
			}
		}
	}

	for(int i = 0; i < numObjs; i++)
		delete groups[i];


	//Shutdown everything here
	d3d->Shutdown();



	UnregisterClass(WINDOW_CLASS_NAME,hInstance);

	return (int)(msg.wParam);

}

BOOL RegisterWindowClass(HINSTANCE hInstance){
	WNDCLASSEX winClassEx;

	winClassEx.cbSize			= sizeof(winClassEx);
	winClassEx.style			= CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
	winClassEx.lpfnWndProc		= WindowProc;
	winClassEx.cbClsExtra		= 0;
	winClassEx.cbWndExtra		= 0;
	winClassEx.hInstance		= hInstance;
	winClassEx.hIcon			= LoadIcon(NULL, IDI_APPLICATION);
	winClassEx.hIconSm			= NULL;
	winClassEx.hCursor			= LoadCursor(NULL,IDC_ARROW);
	winClassEx.hbrBackground	= (HBRUSH)GetStockObject(BLACK_BRUSH);
	winClassEx.lpszMenuName		= NULL;
	winClassEx.lpszClassName	= WINDOW_CLASS_NAME;

	return RegisterClassEx(&winClassEx);
}

BOOL IsAlreadyRunning(){
	HWND hOtherWnd = FindWindow(WINDOW_CLASS_NAME,WINDOW_TITLE);

	if(hOtherWnd){
		if(IsIconic(hOtherWnd) == TRUE)
			ShowWindow(hOtherWnd, SW_RESTORE);
		SetForegroundWindow(hOtherWnd);

		return TRUE;
	}
	return FALSE;
}

HWND MakeWindow(HINSTANCE hInstance){

	DWORD windowStyleFlags = WS_VISIBLE | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;

	RECT rWindow;
	rWindow.left	= 0;
	rWindow.top		= 0;
	rWindow.right	= WINDOW_WIDTH;
	rWindow.bottom	= WINDOW_HEIGHT;

	AdjustWindowRectEx(&rWindow,windowStyleFlags,FALSE,WS_EX_APPWINDOW);

	int windowWidth = rWindow.right - rWindow.left;
	int windowHeight = rWindow.bottom - rWindow.top;

	return CreateWindowEx(WS_EX_APPWINDOW,WINDOW_CLASS_NAME,WINDOW_TITLE,
		windowStyleFlags, (GetSystemMetrics(SM_CXSCREEN) - windowWidth)/2,
		(GetSystemMetrics(SM_CYSCREEN) - windowHeight)/2,windowWidth,windowHeight,
		HWND_DESKTOP,NULL,hInstance,NULL);
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){
	if (GetAsyncKeyState(VK_ESCAPE))
		msg = WM_DESTROY;

	switch(msg){
	case WM_CREATE:
		{

		}
		break;

	case WM_DESTROY:
		PostQuitMessage(0);
		break;

		//Ignore the ALT popup menu(system menu)
	case WM_SYSKEYUP:
	case WM_SYSCHAR:
		{

		}
		break;

		//Handle ALT+F4
	case WM_CLOSE:
		DestroyWindow(hWnd);
		break;

		//Gain/lose focus
	case WM_ACTIVATE:
		{
			if(LOWORD(wParam) != WA_INACTIVE) // Gaining focus
			{
				//Unpause
			}
			else {							  //Losing focus
				//Pause
			}
		}
		break;

	case WM_PAINT:		//Ignore repaint
		{
			PAINTSTRUCT ps;
			HDC		hdc;

			//Start painting
			hdc = BeginPaint(hWnd,&ps);

			//End Painting
			EndPaint(hWnd,&ps);
		}
		break;

	default:
		return DefWindowProc(hWnd, msg, wParam, lParam);
	}

	return 0;
}

void HandleInput(View* camera,float dt){
	SCENE scene = *camera->GetStruct();
	XMMATRIX view = XMMatrixInverse(NULL, XMLoadFloat4x4(&scene.mView));
	static POINT mousePosition;
	static POINT currentPosition;
	static bool pressed = false;


	float moveSpeed = dt;

	//Translate along the local +Z axis
	if (GetAsyncKeyState('W'))
		view = XMMatrixTranslation(0.0f, 0.0f, 5.0f * moveSpeed) * view;

	//Translate along the local -Z axis
	if (GetAsyncKeyState('S'))
		view = XMMatrixTranslation(0.0f, 0.0f, -5.0f * moveSpeed) * view;

	//Translate along the local -X axis
	if (GetAsyncKeyState('A'))
		view = XMMatrixTranslation(-5.0f * moveSpeed, 0.0f, 0.0f) * view;

	//Translate along the local +X axis
	if (GetAsyncKeyState('D'))
		view = XMMatrixTranslation(5.0f * moveSpeed, 0.0f, 0.0f) * view;

	//Translate along the global +Y axis
	if (GetAsyncKeyState(VK_SHIFT))
		view *= XMMatrixTranslation(0.0f, -5.0f * moveSpeed, 0.0f);

	//Translate along the global +Y axis
	if (GetAsyncKeyState(VK_SPACE))
		view *= XMMatrixTranslation(0.0f, 5.0f * moveSpeed, 0.0f);

	if(GetAsyncKeyState('U')){
		std::wstringstream output;
		XMVECTOR currentCameraPosition = view.r[3];
		XMFLOAT3 ccp;
		XMStoreFloat3(&ccp,currentCameraPosition);
		output << "Current Cam Pos: " << ccp.x << ", " << ccp.y << ", " << ccp.z << std::endl;
		OutputDebugString(output.str().c_str());
	}

	// if the right mouse button is NOT pressed track the mouse
	if ((GetKeyState(VK_RBUTTON) >> 15) == 0){
		GetCursorPos(&mousePosition);
	}
	// if the right mouse button IS pressed rotate the camera 
	// based on the distance the mouse has moved from its previous position.
	if (GetAsyncKeyState(VK_RBUTTON)){

		POINT currentPosition;
		GetCursorPos(&currentPosition);

		XMFLOAT2 rotation = XMFLOAT2((FLOAT) (currentPosition.y - mousePosition.y), (FLOAT) (currentPosition.x - mousePosition.x));

		GetCursorPos(&mousePosition);

		XMVECTOR currentCameraPosition = view.r[3];
		view.r[3] = XMVectorZero();
		view *= RotateY(rotation.y); //Global
		view.r[3] = currentCameraPosition;
		view = RotateX(rotation.x) * view; //Local
	}

	XMStoreFloat4(&scene.camPos,view.r[3]);
	XMStoreFloat4x4(&scene.mView, XMMatrixInverse(NULL, view));
	camera->UpdateStruct(scene);
}




